package com.smsc.headend.task.engine.executor.impl.ansi;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import com.smsc.headend.common.enums.errorcode.TaskExecuteErrorCode;
import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.module.asset.dmo.ComConfigDMO;
import com.smsc.headend.module.asset.entity.Meter;
import com.smsc.headend.module.common.entity.DeviceOperation;
import com.smsc.headend.module.data.dmo.DeviceSAPandKeys;
import com.smsc.headend.module.data.dto.AnsiSubDataItemDTO;
import com.smsc.headend.module.data.dto.DataItemDTO;
import com.smsc.headend.module.data.enums.DictionaryType;
import com.smsc.headend.module.protocol.cosem.util.ByteUtils;
import com.smsc.headend.module.report.entity.LogScheduleTaskDevice;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.AtomicTaskState;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.module.task.enums.TaskType;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskExecutorException;
import com.smsc.headend.task.engine.executor.TaskExecutor;
import com.smsc.headend.task.engine.facade.DataItemFacade;
import com.smsc.headend.task.engine.handler.impl.ansi.ReadAnsiEdModeHandler;
import com.smsc.headend.task.engine.handler.impl.collection.ReadEventLogBufferHandler;
import com.smsc.headend.task.engine.service.ConnectorService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import com.smsc.headend.task.engine.service.collection.CollectTaskService;
import com.smsc.headend.task.engine.service.task.LogScheduleTaskDeviceService;
import com.smsc.headend.task.engine.service.task.TaskPacketService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;

@Slf4j
public class ReadAnsiLogBookExecutor implements TaskExecutor {
    @Autowired
    TaskPacketService packetService;
    @Autowired
    ConnectorService connectorService;

    @Autowired
    RedisUtils redisUtils;
    @Autowired
    FeignAssetManagementService assetManagementRemoteService;
    @Autowired
    DataItemFacade dataItemFacade;

    @Autowired
    KafkaTemplate kafkaTemplate;
    @Autowired
    TaskDAO taskDAO;
    @Autowired
    AssetService assetService;
    @Autowired
    LogScheduleTaskDeviceService logScheduleTaskDeviceService;
    @Autowired
    CollectTaskService collectTaskService;

    @Override
    public AtomicTaskState execute(Task task, AtomicTask atomicTask) throws UDISTaskExecutorException {
        Meter meter = getMeter(task, assetService);
        DeviceOperation dictionary = assetService.getDictionary(atomicTask.getOperationId(), DictionaryType.Operation.getType());
        ComConfigDMO comConfigDMO = assetService.getComConfigById(task.getComId());
        List<DataItemDTO> dataItemCosemList = dataItemFacade.getDataItemsByOperation(meter, null, dictionary, getParameterMap(task, atomicTask), comConfigDMO == null ? null : comConfigDMO.getModelId());
        if (CollectionUtils.isEmpty(dataItemCosemList)) {
            throw new UDISTaskExecutorException(TaskExecuteErrorCode.DATAITEM_NOT_EXISTS.toString());
        }
        AnsiSubDataItemDTO dataItemDTO = (AnsiSubDataItemDTO) dataItemCosemList.get(0);
        DeviceSAPandKeys deviceKeys = assetManagementRemoteService.getMeterCommunicationKey(meter.getMeterId(), dataItemDTO.getRoleId());
        AtomicTaskState atomicState;
        // read ed mode status
        if(1024000002102L == dataItemDTO.getSubDataItemId()) {
            atomicState = createAtomicState(ReadAnsiEdModeHandler.class);
        } else {
            atomicState = taskDAO.hashGetAtomicTaskState(atomicTask.getAtomicTaskNo());
            Map<String, Object> parameterMap = getParameterMap(task, atomicTask);
            if (atomicState == null) {
                atomicState = createAtomicState(ReadEventLogBufferHandler.class);
                Map<String, Object> initialedMap = collectTaskService.scheduleTaskTimeInit(task, parameterMap, meter, task.getTaskType());
                Long profileStartTime = MapUtil.getLong(initialedMap, "profileStartTime");
                Long profileEndTime = MapUtil.getLong(initialedMap, "profileEndTime");
                Map data = MapUtil.newHashMap();
                data.put("profileStartTime", profileStartTime);
                data.put("profileEndTime", profileEndTime);
                atomicState.setData(data);
            } else {
                Map data = atomicState.getData();
                int currentOffSet;
                if (!data.containsKey("CURRENT_OFF_SET")) {
                    // calculate off set of last event
                    Integer lastEntryElement = MapUtil.getInt(data, "LAST_ENTRY_ELEMENT");
                    currentOffSet = 11 + lastEntryElement * 28;
                    data.put("CURRENT_OFF_SET", currentOffSet);
                } else {
                    currentOffSet = MapUtil.getInt(data, "CURRENT_OFF_SET");
                    currentOffSet -= 28;
                    // is the start event
                    if (currentOffSet < 11) {
                        currentOffSet = 299 * 28 + 11;
                        atomicState.setStored(true);
                    }
                    atomicState.getData().put("CURRENT_OFF_SET", currentOffSet);
                }
                dataItemDTO.setTableOffset(currentOffSet);
                dataItemDTO.setByteCount(28);
            }
        }

        byte[] packets = packetService.buildReadOneAttributePacket(dataItemDTO, comConfigDMO, deviceKeys);
        log.debug(ByteUtils.byteToHexString(packets));
        connectorService.sendPacket(task, atomicTask, dataItemCosemList.get(0).getRoleId(), task.getIsODR(), packets);

        return atomicState;
    }

    @Override
    public Boolean handleFailure(Task task, AtomicTask atomicTask, String message) {
        log.info("error {}, ", message);
        if (task.getIsODR()) {
            Map<String, Object> param = getParameterMap(task, atomicTask);
            Long profileDataItemId = Long.valueOf(String.valueOf(param.get("dataItemId")));
            // key 回曲线的dataItemId给web
            taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), Convert.toStr(profileDataItemId), message, 3600l, false);
        }
        if ((TaskType.ScheduleRead_Profile.toString().equalsIgnoreCase(task.getTaskType()) && !message.equals(TaskExecuteErrorCode.SCHEDULE_TASK_IS_LATEST_PROCESS.toString()))
                || TaskType.EventRead_Profile.toString().equalsIgnoreCase(task.getTaskType())
                || TaskType.ScheduleRead_LogBook.toString().equalsIgnoreCase(task.getTaskType())) {
            logScheduleTaskDeviceService.sendMessage(task, LogScheduleTaskDevice.StatusEnum.Failed.getCode(), LogScheduleTaskDevice.StatusDescEnum.DecodeDataFailed.getCode());
        }
        return true;
    }
}
